﻿import org.swxformat.*;

// Note: MTASC requires that the class name be fully qualified
// even if we import the package its in. (Beta 1.1 fix submitted by Wouter.)
class org.swxformat.ExternalAsset extends EventfulObject
{
	// You may want to listen for this event if you want to do something
	// the moment the content has loaded instead of a frame later when 
	// the content becomes available for referencing. 
	public static var LOADED_BUT_NOT_READY:String = "loadedButNotReady";
	
	public static var LOAD:String = "load";
	public static var PROGRESS:String = "progress";
	public static var TIMEOUT:String = "timeout";
	public static var FAULT:String = "fault";
	
	public static var POST:String = "POST";
	public static var GET:String = "GET";
	
	public static var verbose:Boolean = true;
	
	private var _holder:MovieClip = null;
	private var _innerHolder:MovieClip = null;
	
	private var url:String;
	private var _dataObject:Object = null;
	
	private var encoding:String = ExternalAsset.POST;
	private var resultObj:Object;
	private var resultFn:Function;
	private var progressObj:Object;
	private var progressFn:Function;
	
	private var checkLoadProgressInterval;
	
	private var _startTime:Number = null;
	private var _timeoutInSeconds:Number = 30;
	
	private var _loaded:Boolean = false;
	private var _cancelled:Boolean = false;
	private var _loading:Boolean = false;
	
	function ExternalAsset (holder:MovieClip, url:String, data:Object, encoding:String)
	{
		// If not in verbose mode, do not output status/error messages.
		if (!ExternalAsset.verbose) ExternalAsset.echo = null;
		
		this.holder = holder;
		this.url = url;
		_dataObject = data;
		setEncoding(encoding);
	}
	
	public function toString()
	{
		echo("ExternalAssset instance. Clip: "+clip);
	}
	
	public function setResultHandler (resultObj:Object, resultFn:Function)
	{
		this.resultObj = resultObj;
		this.resultFn = resultFn;
	}
	
	public function setProgressHandler (progressObj:Object, progressFn:Function)
	{
		this.progressObj = progressObj;
		this.progressFn = progressFn;
	}
	
	public function setEncoding (encoding:String)
	{
		if (encoding != ExternalAsset.POST && encoding != ExternalAsset.GET)
		{
			echo ("Warning: Encoding must be either POST or GET. Keeping current setting ("+this.encoding+")");
		}
		else
		{
			this.encoding = encoding;
		}
	}
	
	public function setTimeout (timeout:Number)
	{
		if (timeout > 0)
		{
			_timeoutInSeconds = timeout;
		}
	}
	
	public function get result():MovieClip
	{
		return _holder.innerHolder.result;
	}
	
	public function get hasHolder():Boolean
	{
		return (_holder != null);
	}
	
	public function set holder(holder:MovieClip):Void
	{
		_holder = holder;
		
		_holder.createEmptyMovieClip("innerHolder", 1);
		_innerHolder = _holder["innerHolder"];
	}
	
	public function get loaded():Boolean
	{
		return _loaded;
	}
	
	public function get isCancelled():Boolean
	{
		return _cancelled;
	}
	
	public function get isLoading():Boolean
	{
		return _loading;
	}
	
	public function get holder():MovieClip
	{
		return _holder;
	}
	
	public function get clip():MovieClip
	{
		return _holder.innerHolder;
	}
	
	public function load():Void
	{
		_innerHolder._visible = false;
		
		// Copy any data to be sent to the server into the holder movie clip
		for (var i in _dataObject)
		{
			_innerHolder[i] = _dataObject[i];
		}
				
		// Load the movie
		if (_dataObject == null)
		{
			echo("ExternalAsset.load() info: Loading non-SWX asset (image, SWF, etc.)")
			_innerHolder.loadMovie(url);
		}
		else
		{
			echo("ExternalAsset.load() info: Sending data using " + encoding + " encoding.");
			
			// Encode data in JSON format for use in SWX.
			SWX.prepare(_innerHolder);
			
			_innerHolder.loadMovie(url, encoding);	
		}
		
		// Set flag
		_loading = true;

		// Set the start time of the load
		_startTime = getTimer();
			
		// Check for load every 250 ms
		checkLoadProgressInterval = setInterval(this, "checkLoadProgress", 250);
	}
	
	public function cancel():Boolean
	{
		if (_loaded)
		{
			echo("Warning: Cannot cancel loading clip since it has already loaded: " + url);
			return false;
		}
		else
		{
			echo("Cancelling load on " + _innerHolder);
			// Cancel the current load
			clearInterval(checkLoadProgressInterval);
			
			_innerHolder.unloadMovie();
			//_innerHolder.removeMovieClip();
			//_innerHolder.loadMovie("DoesNotExist.swf");
			
			_cancelled = true;
			_loading = false;
			return true;
		}
	}
	
	//
	// Private methods
	//
	
	// Static methods
	
	// echos the passed string. (Gets nulled out in non-verbose mode.)
	static private function echo(msg:String)
	{
		trace (msg);
	}
	
	// Instance methods
	
	private function checkLoadProgress()
	{
		var bytesLoaded:Number = _innerHolder.getBytesLoaded();
		var bytesTotal:Number = _innerHolder.getBytesTotal();

		// Check for timeout
		var currentTime:Number = getTimer();

		if (((currentTime-_startTime)/1000) > _timeoutInSeconds)
		{
			// Timeout
			echo("ExternalAsset timeout: " + _holder);
			clearInterval(checkLoadProgressInterval);
			
			// Re-create the inner holder clip in case user wants to reuse the same holder.
			_innerHolder.removeMovieClip();
			_holder.createEmptyMovieClip("_innerHolder", 1);
			
			_loading = false;
						
			dispatchEvent({type:ExternalAsset.TIMEOUT});
		}
				
		if (bytesLoaded > 4 && (bytesLoaded == bytesTotal))
		{
			echo("ExternalAsset loaded: " + _holder);
			
			_innerHolder._visible = true;
			
			clearInterval(checkLoadProgressInterval);
			
			_loaded = true;
			_loading = false;
			
			// Dispatch an event now in case someone needs it.
			dispatchEvent({type: LOADED_BUT_NOT_READY, clip:clip});
			
			// Dispatch the load event on the next frame so that 
			// developers don't have to wait a frame themselves for things
			// to initialize and become available. 
			_holder.objectRef = this;
			_holder.functionRef = dispatchEventLater;
			_holder.onEnterFrame = function ()
			{
				this.functionRef.apply(this.objectRef);
				this.onEnterFrame = null;
			};
		}
		else
		{
			dispatchEvent({type:ExternalAsset.PROGRESS, bytesLoaded:bytesLoaded, bytesTotal:bytesTotal, clip:clip })
		}
	}
	
	private function dispatchEventLater()
	{
		if (clip.result.error)
		{
			// An error was returned, raise a FAULT event. 
			dispatchEvent({type:ExternalAsset.FAULT, clip:clip, fault:result});	
		}
		else
		{
			// Result returned, raise a LOAD event.
			dispatchEvent({type:ExternalAsset.LOAD, clip:clip, result:result});			
		}
		
	}
}